﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using System.Xml.Linq;
using BulldogCMS.Core.Storage.DataTypes;
using BulldogCMS.Core.Exceptions;

namespace BulldogCMS.Core.Storage.Providers
{
    public class LocalContentTreeProvider : BaseContentTreeProvider, IContentTreeProvider
    {
        private string _storagePath = null;
        private static string STORAGE_CONTENT_DIR =     "Content";

        public IEnumerable<Node> GetRootNodes(long? nodeID = null)
        {
            if (_storagePath != null)
            {
                string rootPath = Path.Combine(_storagePath,STORAGE_CONTENT_DIR);
                return this.GetNodesFromPath(rootPath, null, null);
            }
            return new List<Node>();
        }

        public IEnumerable<Node> GetNodes(Node parentNode)
        {
            string currentProviderPathDiskPath = parentNode.CurrentProviderPath.Replace('/','\\');
            string rootPath = Path.Combine(_storagePath, STORAGE_CONTENT_DIR, currentProviderPathDiskPath);
            return this.GetNodesFromPath(rootPath,parentNode.CurrentProviderPath,parentNode.ContentProviderTypeName);

            //return null; //TODO    
        }

        public void SetParameter(string name, string value)
        {
            if ("storagePath".Equals(name,StringComparison.InvariantCultureIgnoreCase))
            {
                if (value.StartsWith("~/"))
                {
                    this._storagePath = HttpContext.Current.Server.MapPath(value);
                }
                else
                {
                    this._storagePath = value;
                }
            }
        }

        // C:\
        private IEnumerable<Node> GetNodesFromPath(string directoryPath, string currentProviderPath, string currentProviderTypeName)
        {
            List<Node> nodes = new List<Node>();
            DirectoryInfo dInfo = new DirectoryInfo(directoryPath);
            
            foreach (DirectoryInfo subDir in dInfo.GetDirectories())
            {
                string metaFileFullPath = Path.Combine(subDir.FullName, string.Format("{0}Meta.xml",subDir.Name));
                Node node = new Node();
                XDocument metaDoc = XDocument.Load(metaFileFullPath);
                this.FillSimpleData(node, metaDoc);
                this.FillComplexData(node, metaDoc);

                node.Name = subDir.Name;

                if (currentProviderPath == null)
                {
                    node.CurrentProviderPath = node.Name;
                }
                else
                {
                    node.CurrentProviderPath = currentProviderPath.CombineUrl(node.Name);
                }

                if (node.ContentTreeProvider == null)
                {
                    node.ContentTreeProvider = this;
                    node.ContentProviderTypeName = currentProviderTypeName;
                }
                nodes.Add(node);
            }

            return nodes;
        }

        public NodeVersion GetNodeVersion(Node node, string languageIdent, int versionNumber)
        {
            string currentProviderPathDiskPath = node.CurrentProviderPath.Replace('/', '\\');
            string versionFileName = string.Format("{0}Version({1},{2}).xml", node.Name, languageIdent, versionNumber);
            string filePath = Path.Combine(_storagePath, STORAGE_CONTENT_DIR, currentProviderPathDiskPath, versionFileName);
            XDocument versionMetaDoc = XDocument.Load(filePath);
            NodeVersion version = new NodeVersion();

            base.FillVersionSimpleData(version, versionMetaDoc);
            base.FillVersionComplexData(version, versionMetaDoc);

            return version;
        }

        public Node GetNode(string currentProviderPath, string name, long parentNodeID)
        {
            string currentProviderPathDiskPath = currentProviderPath.Replace('/', '\\');
            string filePath = Path.Combine(_storagePath, STORAGE_CONTENT_DIR, currentProviderPathDiskPath, name, string.Format("{0}Meta.xml", name));
            if (File.Exists(filePath))
            {
                Node node = new Node();
                XDocument xDoc = XDocument.Load(filePath);
                this.FillSimpleData(node, xDoc);
                this.FillComplexData(node, xDoc);
                node.Name = name;
                return node;
            }
            throw new DataSourceException(string.Format("Node with path {0} not found.", name));
        }

        #region editing

        public Node CreateNewNode(Node node)
        {
            string currentProviderPathDiskPath = node.CurrentProviderPath.Replace('/', '\\');
            string dirPath = Path.Combine(_storagePath, STORAGE_CONTENT_DIR, currentProviderPathDiskPath);
            string metaFileName = Path.Combine(dirPath, string.Format("{0}Meta.xml",node.Name));

            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath);
            }

            using (StreamWriter sWriter = File.CreateText(metaFileName))
            {
                XDocument xDoc = this.GetXDocumentFromNode(node);
                xDoc.Save(sWriter);
            }
            return node;
        }

        // <nodeMetadata ID="3276900" template="BulldogCMS.Core.Templates.BaseTemplate">
        private XDocument GetXDocumentFromNode(Node node)
        {
            XDocument xDoc = new XDocument(
                new XElement("nodeMetadata")
            );
            xDoc.Root.SetAttributeOrLeaveEmpty<long>("ID", node.ID);
            xDoc.Root.SetAttributeOrLeaveEmpty<string>("template", node.TemplateName);

            if (node.VersionsMeta.Count > 0)
            {
                XElement langaugesEl = new XElement("languages");
                foreach (var versionMeta in node.VersionsMeta)
                {
                    langaugesEl.Add(new XElement(
                        "language",
                        new XAttribute("ident", versionMeta.LanguageIdent),
                        new XElement("version",
                            new XAttribute("number", "1"),
                            new XAttribute("ID", versionMeta.ID),
                            new XAttribute("state", versionMeta.State.ToString())
                        )
                    ));
                }
                xDoc.Root.Add(langaugesEl);
            }
            return xDoc;
        }

        public Node UpdateNode(Node node)
        {
            throw new NotImplementedException();
        }

        public void DeleteNode(Node node)
        {
            throw new NotImplementedException();
        }

        public NodeVersion CreateNewVersion(Node node, NodeVersion newVersion)
        {
            string currentProviderPathDiskPath =    node.CurrentProviderPath.Replace('/', '\\');
            string versionFileName =                string.Format("{0}Version({1},{2})", node.Name, newVersion.LanguageName, newVersion.VersionNumber);
            string dirPath =                        Path.Combine(_storagePath, STORAGE_CONTENT_DIR, currentProviderPathDiskPath);
            string filePath =                       Path.Combine(dirPath, versionFileName);

            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath);
            }

            using (StreamWriter sWriter = File.CreateText(filePath))
            {
                XDocument xDoc = this.GetXDocumentFromNodeVersion(newVersion);
                xDoc.Save(sWriter);
            }

            return newVersion;
        }

        private XDocument GetXDocumentFromNodeVersion(NodeVersion nodeVersion)
        {
            XDocument xDoc = new XDocument(
                new XElement("version",
                    new XAttribute("ID", nodeVersion.ID),
                    new XAttribute("version", nodeVersion.VersionNumber),
                    new XAttribute("language", nodeVersion.LanguageName)
                )
            );

            if (nodeVersion.FieldValues.Count() > 0)
            {
                XElement fieldsEl = new XElement("fields");
                foreach (var field in nodeVersion.FieldValues) {
                    XElement fieldEl = field.Value.ToXElement();
                    fieldsEl.Add(fieldEl);
                }
                xDoc.Root.Add(fieldsEl);
            }

            return xDoc;
        }

        public NodeVersion UpdateVersion(Node node, NodeVersion updatedVersion)
        {
            throw new NotImplementedException();
        }

        public NodeVersion DeleteVersion(Node node, NodeVersion versionToBeDeleted)
        {
            throw new NotImplementedException();
        }

        #endregion

        public bool ReadOnly
        {
            get { return false; }
        }
    }
}